﻿using System;
using System.Web.Compilation;
using System.Web.UI;
using System.CodeDom;
using System.Web;
using System.Security.Permissions;
using System.Web.Configuration;
using System.ComponentModel;
using System.Globalization;
using System.Diagnostics;
namespace Medianamik.Globalization
{
    [ExpressionPrefix("MedianamikResources")]
    public class MedianamikResourceExpressionBuilder : System.Web.Compilation.ExpressionBuilder
    {
        public override object ParseExpression(string expression, Type propertyType, ExpressionBuilderContext context)
        {
            if (string.IsNullOrEmpty(expression))
            {
                throw new ArgumentException(String.Concat("Too few parameters in expression:", expression));
            }

            MedianamikResourceExpressionFields fields = null;

            string classKey = null;
            string resourceKey = null;

            string[] expParams = expression.Split(new char[] { ',' });
            if (expParams.Length > 2)
            {
                throw new ArgumentException(String.Concat("Too many parameters in expression:", expression));
            }
            if (expParams.Length == 1)
            {
                throw new ArgumentException(String.Concat("Too few parameters in expression:", expression));
            }
            else
            {
                classKey = expParams[0].Trim();
                resourceKey = expParams[1].Trim();
            }

            fields = new MedianamikResourceExpressionFields(classKey, resourceKey);


            //Normalement, cette méthode doit vérifier l'existance de la ressource.
            //Dans notre cas, on retourne [ key ] si la ressource n'existe pas
            //donc pas besoin de faire cette vérification

            //ResourceExpressionBuilder.EnsureResourceProviderFactory();

            //IResourceProvider rp = ResourceExpressionBuilder.
            //    s_resourceProviderFactory.CreateGlobalResourceProvider(fields.ClassKey);

            //object res = rp.GetObject(fields.ResourceKey, CultureInfo.InvariantCulture);

            //if (res == null)
            //{
            //    throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentUICulture, Properties.Resources.RM_ResourceNotFound, fields.ResourceKey));
            //}

            return fields;
        }

        public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
        {
            MedianamikResourceExpressionFields fields = parsedData as MedianamikResourceExpressionFields;

            return GetAppResCodeExpression(fields.ClassKey, fields.ResourceKey, entry);
        }

        public override object EvaluateExpression(object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
        {
            MedianamikResourceExpressionFields fields = parsedData as MedianamikResourceExpressionFields;

            // If the property is not of type string, it will need to be converted
            if (entry.Type != typeof(string) && entry.Type != null)
            {
                return GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, entry.DeclaringType, entry.PropertyInfo.Name);
            }


            return GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey);
        }

        private static object ObjectFromString(string value, Type objType, string propName)
        {

            // Get the PropertyDescriptor for the property
            PropertyDescriptor pd = TypeDescriptor.GetProperties(objType)[propName];
            Debug.Assert(pd != null);
            if (pd == null) return null;

            // Get its type descriptor 
            TypeConverter converter = pd.Converter;
            Debug.Assert(converter != null);
            if (converter == null) return null;

            // Perform the conversion 
            return converter.ConvertFromInvariantString(value);
        }

        private CodeExpression GetAppResCodeExpression(string classKey, string resourceKey, BoundPropertyEntry entry)
        {
            // We generate the following
            //      this.GetGlobalResourceObject(classKey) 
            CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression();
            expr.Method.TargetObject = new CodeTypeReferenceExpression(typeof(MedianamikResourceExpressionBuilder));

            expr.Method.MethodName = "GetGlobalResourceObject";
            expr.Parameters.Add(new CodePrimitiveExpression(classKey));
            expr.Parameters.Add(new CodePrimitiveExpression(resourceKey));

            // If the property is not of type string, it will need to be converted
            if (entry.Type != typeof(string) && entry.Type != null)
            {
                expr.Parameters.Add(new CodeTypeOfExpression(entry.DeclaringType));
                expr.Parameters.Add(new CodePrimitiveExpression(entry.PropertyInfo.Name));
            }

            return expr;
        }

        public static object GetGlobalResourceObject(string classKey, string resourceKey)
        {
            return GetGlobalResourceObject(classKey, resourceKey, null /*objType*/, null /*propName*/);
        }

        public static object GetGlobalResourceObject(string classKey,
            string resourceKey, Type objType, string propName)
        {
            return GetResourceObject(classKey, resourceKey, objType, propName);
        }


        internal static object GetResourceObject(string classKey, string resourceKey, Type objType, string propName)
        {
            string result = Translator.Translate(classKey, resourceKey);

            // If no objType/propName was provided, return the object as is
            if (objType == null)
                return (object)result;

            // If they were provided, perform the appropriate conversion
            return ObjectFromString(result, objType, propName);
        }

    }

    // Holds the fields parsed from a resource expression (e.g. classKey, resourceKey) 
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public sealed class MedianamikResourceExpressionFields
    {
        private string _classKey;
        private string _resourceKey;

        internal MedianamikResourceExpressionFields(string classKey, string resourceKey)
        {
            _classKey = classKey;
            _resourceKey = resourceKey;
        }

        public string ClassKey
        {
            get
            {
                if (_classKey == null)
                {
                    return String.Empty;
                }

                return _classKey;
            }
        }

        public string ResourceKey
        {
            get
            {
                if (_resourceKey == null)
                {
                    return String.Empty;
                }

                return _resourceKey;
            }
        }
    }
}


